{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=\"red\">注</font>: 使用 tensorboard 可视化需要安装 tensorflow (TensorBoard依赖于tensorflow库，可以任意安装tensorflow的gpu/cpu版本)\n",
    "\n",
    "```shell\n",
    "pip install tensorflow-cpu\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:38:45.279754Z",
     "start_time": "2025-01-31T15:38:40.414232Z"
    }
   },
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=12, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.1\n",
      "torch 2.5.1+cpu\n",
      "cpu\n"
     ]
    }
   ],
   "execution_count": 1
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据准备"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:38:47.219350Z",
     "start_time": "2025-01-31T15:38:45.280889Z"
    }
   },
   "source": [
    "from torchvision import datasets\n",
    "from torchvision.transforms import ToTensor\n",
    "from torch.utils.data import random_split\n",
    "\n",
    "# fashion_mnist图像分类数据集\n",
    "train_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=True,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "test_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=False,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "# torchvision 数据集里没有提供训练集和验证集的划分\n",
    "# 这里用 random_split 按照 11 : 1 的比例来划分数据集\n",
    "train_ds, val_ds = random_split(train_ds, [55000, 5000], torch.Generator().manual_seed(seed))"
   ],
   "outputs": [],
   "execution_count": 2
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:38:47.228894Z",
     "start_time": "2025-01-31T15:38:47.220348Z"
    }
   },
   "source": [
    "from torchvision.transforms import Normalize\n",
    "\n",
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "def cal_mean_std(ds):\n",
    "    mean = 0.\n",
    "    std = 0.\n",
    "    for img, _ in ds:\n",
    "        mean += img.mean(dim=(1, 2))\n",
    "        std += img.std(dim=(1, 2))\n",
    "    mean /= len(ds)\n",
    "    std /= len(ds)\n",
    "    return mean, std\n",
    "\n",
    "\n",
    "# print(cal_mean_std(train_ds))\n",
    "# 0.2860， 0.3205\n",
    "transforms = nn.Sequential(\n",
    "    Normalize([0.2856], [0.3202])\n",
    ") # 对每个通道进行标准化"
   ],
   "outputs": [],
   "execution_count": 3
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:38:48.907274Z",
     "start_time": "2025-01-31T15:38:48.901098Z"
    }
   },
   "source": [
    "from torch.utils.data.dataloader import DataLoader\n",
    "\n",
    "batch_size = 32\n",
    "# 从数据集到dataloader\n",
    "train_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True, num_workers=4)\n",
    "val_loader = DataLoader(val_ds, batch_size=batch_size, shuffle=False, num_workers=4)\n",
    "test_loader = DataLoader(test_ds, batch_size=batch_size, shuffle=False, num_workers=4)"
   ],
   "outputs": [],
   "execution_count": 4
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:38:55.255307Z",
     "start_time": "2025-01-31T15:38:55.237002Z"
    }
   },
   "source": [
    "class CNN(nn.Module):\n",
    "    def __init__(self, activation=\"relu\"):\n",
    "        super(CNN, self).__init__()\n",
    "        self.activation = F.relu if activation == \"relu\" else F.selu\n",
    "        self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, padding=1)\n",
    "        self.conv2 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=1)\n",
    "        self.pool = nn.MaxPool2d(2, 2)\n",
    "        self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1)\n",
    "        self.conv4 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, padding=1)\n",
    "        self.conv5 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=1)\n",
    "        self.conv6 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=1)\n",
    "        self.flatten = nn.Flatten()\n",
    "        # input shape is (28, 28, 1) so the fc1 layer in_features is 128 * 3 * 3\n",
    "        self.fc1 = nn.Linear(128 * 3 * 3, 128)\n",
    "        self.fc2 = nn.Linear(128, 10)\n",
    "        \n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层、卷积层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, (nn.Linear, nn.Conv2d)):\n",
    "                nn.init.xavier_uniform_(m.weight)\n",
    "                nn.init.zeros_(m.bias)\n",
    "        \n",
    "    def forward(self, x):\n",
    "        act = self.activation\n",
    "        x = self.pool(act(self.conv2(act(self.conv1(x)))))\n",
    "        x = self.pool(act(self.conv4(act(self.conv3(x)))))\n",
    "        x = self.pool(act(self.conv6(act(self.conv5(x)))))\n",
    "        x = self.flatten(x)\n",
    "        x = act(self.fc1(x))\n",
    "        x = self.fc2(x)\n",
    "        return x\n",
    "    \n",
    "\n",
    "for idx, (key, value) in enumerate(CNN().named_parameters()):\n",
    "    print(f\"{key}\\tparamerters num: {np.prod(value.shape)}\")\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "conv1.weight\tparamerters num: 288\n",
      "conv1.bias\tparamerters num: 32\n",
      "conv2.weight\tparamerters num: 9216\n",
      "conv2.bias\tparamerters num: 32\n",
      "conv3.weight\tparamerters num: 18432\n",
      "conv3.bias\tparamerters num: 64\n",
      "conv4.weight\tparamerters num: 36864\n",
      "conv4.bias\tparamerters num: 64\n",
      "conv5.weight\tparamerters num: 73728\n",
      "conv5.bias\tparamerters num: 128\n",
      "conv6.weight\tparamerters num: 147456\n",
      "conv6.bias\tparamerters num: 128\n",
      "fc1.weight\tparamerters num: 147456\n",
      "fc1.bias\tparamerters num: 128\n",
      "fc2.weight\tparamerters num: 1280\n",
      "fc2.bias\tparamerters num: 10\n"
     ]
    }
   ],
   "execution_count": 5
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:38:58.115362Z",
     "start_time": "2025-01-31T15:38:58.110285Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 计算总参数量，numel()返回张量元素个数，\n",
    "def count_parameters(model):\n",
    "    return sum(p.numel() for p in model.parameters() if p.requires_grad)"
   ],
   "outputs": [],
   "execution_count": 6
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:38:58.387613Z",
     "start_time": "2025-01-31T15:38:58.372186Z"
    }
   },
   "cell_type": "code",
   "source": "count_parameters(CNN())",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "435306"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 7
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练\n",
    "\n",
    "pytorch的训练需要自行实现，包括\n",
    "1. 定义损失函数\n",
    "2. 定义优化器\n",
    "3. 定义训练步\n",
    "4. 训练"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:39:02.618154Z",
     "start_time": "2025-01-31T15:39:02.560607Z"
    }
   },
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ],
   "outputs": [],
   "execution_count": 8
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### TensorBoard 可视化\n",
    "\n",
    "\n",
    "训练过程中可以使用如下命令启动tensorboard服务。\n",
    "\n",
    "```shell\n",
    "tensorboard \\\n",
    "    --logdir=runs \\     # log 存放路径\n",
    "    --host 0.0.0.0 \\    # ip\n",
    "    --port 8848         # 端口\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:39:09.695938Z",
     "start_time": "2025-01-31T15:39:06.031263Z"
    }
   },
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)"
   ],
   "outputs": [],
   "execution_count": 9
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Save Best\n"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:39:10.883477Z",
     "start_time": "2025-01-31T15:39:10.878303Z"
    }
   },
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))"
   ],
   "outputs": [],
   "execution_count": 10
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Early Stop"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:39:14.339041Z",
     "start_time": "2025-01-31T15:39:14.333909Z"
    }
   },
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience"
   ],
   "outputs": [],
   "execution_count": 11
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:54:51.830309Z",
     "start_time": "2025-01-31T15:39:22.062337Z"
    }
   },
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                    \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 20\n",
    "\n",
    "activation = \"selu\"\n",
    "model = CNN(activation)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用SGD\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "tensorboard_callback = TensorBoardCallback(f\"runs/cnn-{activation}\")\n",
    "tensorboard_callback.draw_model(model, [1, 1, 28, 28])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(f\"checkpoints/cnn-{activation}\", save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=10)\n",
    "\n",
    "model = model.to(device)\n",
    "record = training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=1000\n",
    "    )"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  0%|          | 0/34380 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "f936c5222ade4b3c9ef0adbf0fa278d6"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Early stop at epoch 13 / global_step 24000\n"
     ]
    }
   ],
   "execution_count": 12
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:58:41.058902Z",
     "start_time": "2025-01-31T15:58:40.927190Z"
    }
   },
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
    "        axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=500)  #横坐标是 steps"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ],
      "image/png": "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xzSLcHHPPffYFy5cqKaTLFmyxD5u3Dh7cnKymugR7tshLy9PTWORi/yczz33nLqtT2N56qmn1Hviv//9r/33339X0106depkLyoq8jrR5fnnn7fHx8fbf/31V3s4bQv53L333qsmt8jP/NNPP9kHDx5s79atm724uDistsWtt95qb9asmfo/cejQIeelsLDQ+ZhbbrnF3qFDB/uCBQvsq1atso8YMUJddJUn29x111321q1bqwk4RDXhvknDfRP3TdwvuXDf1DTCKtARL730knpTREZGqpGey5cv9znK8NNPP1V/SL/88kt7OLn00kvtaWlpahu0bdtWfbxjxw5DbAf9P33li4ys1Md4Pvjgg+oPgRx4jB071r5161bn8yv/ERXPPvusPSEhQe2Yw2VbyB/SCRMm2Fu1aqXGV3bs2NF+4403Og+8wmlbeNsGcnnnnXecj5GDidtuu83evHlze2xsrP2CCy5QOxxfOxNxxx13qP9n7u8fIl+4b+K+ifsm7pfccd/UNEzyT1Nlj4iIiIiIiJpC2PToEBERERER6RjoEBERERFR2GGgQ0REREREYYeBDhERERERhR0GOkREREREFHYY6BARERERUdhhoENERERERGGHgQ4REREREYUdBjpEfnLttdfi/PPPD/TLICIiUrhfIqNjoENERERERGGHgQ5RHX3xxRfo168fYmJi0LJlS4wbNw733Xcf3n33Xfz3v/+FyWRSl4ULF6rH79u3D3/605+QlJSEFi1a4LzzzsOePXuqnHGbPn06WrVqhcTERNxyyy0oLS0N4E9JREShgvslIu+sPu4nIi8OHTqEyy67DE8//TQuuOAC5OXl4ddff8XVV1+NjIwM5Obm4p133lGPlZ1HWVkZJk6ciBEjRqjHWa1WPP744zjzzDPx+++/IzIyUj12/vz5iI6OVjsh2dlcd911amf1xBNPBPgnJiKiYMb9EpFvDHSI6rhDKS8vx4UXXoiOHTuq++QsmpAzaSUlJUhNTXU+/oMPPoDNZsObb76pzqYJ2eHIWTTZeUyYMEHdJzuWt99+G7GxsejTpw8effRRdTbuscceg9nMxCsREXnH/RKRb3ynEtXBgAEDMHbsWLUTueSSS/DGG2/g+PHjPh+/fv167NixAwkJCYiPj1cXOaNWXFyMnTt3enxd2Zno5Exbfn6+Ki8gIiLyhfslIt+Y0SGqA4vFgnnz5mHp0qWYO3cuXnrpJfzjH//AihUrvD5edgpDhgzBhx9+WOVzUvdMRETUENwvEfnGQIeojiTVP2rUKHV56KGHVKnA119/rdL8FRUVHo8dPHgwPv30U6SkpKhmzurOsBUVFakyA7F8+XJ1lq19+/aN/vMQEVFo436JyDuWrhHVgZwhe/LJJ7Fq1SrV5PnVV1/hyJEj6NWrF9LT01Uj59atW5Gdna0aPq+44gokJyeriTbS9Ll7925VA/3Xv/4V+/fvd35dmWRz/fXXY9OmTZg9ezYefvhhTJkyhXXQRERULe6XiHxjRoeoDuTs16JFi/DCCy+oSTZy1uzZZ5/FpEmTMHToULWzkGspDfj5558xevRo9fi//e1vqlFUpuG0bdtW1VO7n0mTj7t164bTTjtNNY7KBJ1HHnkkoD8rEREFP+6XiHwz2e12ezWfJ6JGJusV5OTk4Jtvvgn0SyEiIuJ+icIG849ERERERBR2GOgQEREREVHYYekaERERERGFHWZ0iIiIiIgo7DDQISIiIiKisMNAh4iIiIiIwg4DHSIiIiIiCjsMdIiIiIiIKOww0CEiIiIiorDDQIeIiIiIiMIOAx0iIiIiIgo7DHSIiIiIiAjh5v8BVM2ZS6+tGv0AAAAASUVORK5CYII="
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 13
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-01-31T15:59:00.741313Z",
     "start_time": "2025-01-31T15:58:53.739006Z"
    }
   },
   "source": [
    "# dataload for evaluating\n",
    "\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(f\"checkpoints/cnn-{activation}/best.ckpt\",weights_only=True, map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, test_loader, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.2809\n",
      "accuracy: 0.9157\n"
     ]
    }
   ],
   "execution_count": 14
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pytorch",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
